import { useRef, useCallback } from "react";

interface DragInertiaOptions {
  friction?: number; // 摩擦系数，默认0.95
  minVelocity?: number; // 最小速度阈值，默认0.1
  velocityMultiplier?: number; // 速度倍数，默认16
}

interface DragInertiaReturn {
  // 开始拖拽
  startDrag: (clientX: number, clientY: number) => void;
  // 更新拖拽位置
  updateDrag: (
    clientX: number,
    clientY: number
  ) => { dx: number; dy: number } | null;
  // 结束拖拽，开始惯性滑动
  endDrag: () => void;
  // 停止惯性动画
  stopInertia: () => void;
  // 是否正在拖拽
  isDragging: () => boolean;
  // 是否正在惯性滑动
  isInertiaActive: () => boolean;
}

export function useDragInertia(
  onMove: (dx: number, dy: number) => void,
  options: DragInertiaOptions = {}
): DragInertiaReturn {
  const {
    friction = 0.95,
    minVelocity = 0.6,
    velocityMultiplier = 8,
  } = options;

  const isDraggingRef = useRef(false);
  const prevPositionRef = useRef<{ x: number; y: number } | null>(null);
  const velocityRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 });
  const lastMoveTimeRef = useRef<number>(0);
  const animationFrameRef = useRef<number | null>(null);
  const isInertiaActiveRef = useRef(false);

  // 惯性滑动动画
  const startInertiaAnimation = useCallback(() => {
    if (isInertiaActiveRef.current) return;
    isInertiaActiveRef.current = true;

    const animate = () => {
      const velocity = velocityRef.current;

      // 如果速度太小，停止动画
      if (
        Math.abs(velocity.x) < minVelocity &&
        Math.abs(velocity.y) < minVelocity
      ) {
        isInertiaActiveRef.current = false;
        velocityRef.current = { x: 0, y: 0 };
        return;
      }

      // 移动
      onMove(velocity.x, velocity.y);

      // 应用摩擦力，逐渐减速
      velocityRef.current = {
        x: velocity.x * friction,
        y: velocity.y * friction,
      };

      // 继续动画
      animationFrameRef.current = requestAnimationFrame(animate);
    };

    animationFrameRef.current = requestAnimationFrame(animate);
  }, [onMove, friction, minVelocity]);

  // 停止惯性动画
  const stopInertiaAnimation = useCallback(() => {
    if (animationFrameRef.current) {
      cancelAnimationFrame(animationFrameRef.current);
      animationFrameRef.current = null;
    }
    isInertiaActiveRef.current = false;
    velocityRef.current = { x: 0, y: 0 };
  }, []);

  // 开始拖拽
  const startDrag = useCallback(
    (clientX: number, clientY: number) => {
      // 停止当前的惯性动画
      stopInertiaAnimation();

      isDraggingRef.current = true;
      prevPositionRef.current = { x: clientX, y: clientY };
      lastMoveTimeRef.current = Date.now();
      velocityRef.current = { x: 0, y: 0 };
    },
    [stopInertiaAnimation]
  );

  // 更新拖拽位置
  const updateDrag = useCallback(
    (clientX: number, clientY: number) => {
      if (!isDraggingRef.current) return null;

      const prev = prevPositionRef.current;
      if (!prev) {
        prevPositionRef.current = { x: clientX, y: clientY };
        lastMoveTimeRef.current = Date.now();
        return null;
      }

      const currentTime = Date.now();
      const deltaTime = currentTime - lastMoveTimeRef.current;

      const dx = clientX - prev.x;
      const dy = clientY - prev.y;

      // 计算速度（像素/毫秒）
      if (deltaTime > 0) {
        velocityRef.current = {
          x: (dx / deltaTime) * velocityMultiplier, // 转换为每帧的速度（假设60fps）
          y: (dy / deltaTime) * velocityMultiplier,
        };
      }

      prevPositionRef.current = { x: clientX, y: clientY };
      lastMoveTimeRef.current = currentTime;

      return { dx, dy };
    },
    [velocityMultiplier]
  );

  // 结束拖拽，开始惯性滑动
  const endDrag = useCallback(() => {
    if (isDraggingRef.current) {
      // 开始惯性滑动
      startInertiaAnimation();
    }

    isDraggingRef.current = false;
    prevPositionRef.current = null;
  }, [startInertiaAnimation]);

  // 停止惯性动画（外部调用）
  const stopInertia = useCallback(() => {
    stopInertiaAnimation();
  }, [stopInertiaAnimation]);

  // 是否正在拖拽
  const isDragging = useCallback(() => {
    return isDraggingRef.current;
  }, []);

  // 是否正在惯性滑动
  const isInertiaActive = useCallback(() => {
    return isInertiaActiveRef.current;
  }, []);

  return {
    startDrag,
    updateDrag,
    endDrag,
    stopInertia,
    isDragging,
    isInertiaActive,
  };
}
